/*
 * Purpose: Represents a program of source code files that can be compiled and emulated :)
 */
package Model;

import Util.Execute;
import Util.FileSystem;
import java.io.File;
import java.io.FilenameFilter;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JOptionPane;

/**
 *
 * @author jase
 */
public class Program {

	private File path; // location of the program
	private final Map<String, Programmable> files;
	private ActiveObject active;

	public Program() {
		files = new HashMap<String, Programmable>();
		active = new ActiveObject();
		active.start();
	}

	public String getName() {
		return path.getName();
	}

	// returns a list of file names for the project
	public SourceCode[] load(File p) {
		path = p;

		File src = new File(path, "/src"); // NOTE: the source code is in the src subdir in the project dir

		// find the names of all program files in the project directory
		File[] projFiles = src.listFiles(new FilenameFilter() {

			public boolean accept(File dir, String name) {
				return name.matches("[\\w]+\\.java");
			}
		});

		System.out.println("count: " + projFiles.length);
		for (File file : projFiles) {
			System.out.println("name: " + file.getAbsolutePath());
		}

		if (projFiles.length == 0) { // no files yet
			return null;
		}

		SourceCode[] result = new SourceCode[projFiles.length];
		for (int i = 0; i < projFiles.length; ++i) {
			result[i] = new SourceCode();
			String name = projFiles[i].getName().split("\\.java")[0]; // NOTE: dont need to save the .java extension
			result[i].setName(name);
			String text = FileSystem.readAll(projFiles[i].getAbsolutePath());
			result[i].setCode(text);
		}
		return result;
	}

	public File getPath() {
		return path;
	}

	public void addFile(String name, Programmable prog) {
		if (!files.containsKey(name)) {
			System.out.println("adding: " + name);
			files.put(name, prog);
		}
	}

	public void refresh() {
		active.refresh();
	}

	public void removeFile(String name) {
		if (files.containsKey(name)) {
			files.remove(name);
		}
	}

	public boolean hasFile(String name) {
		return files.containsKey(name);
	}

	public void clear() {
		synchronized (files) {
			files.clear();
		}
		path = null;
	}

	public void shutdown() {
		active.shutdown();
		// save all files once more
		for (String name : files.keySet()) {
			Programmable prog = files.get(name);
			String text = prog.getCode();
			System.out.println("last saving to " + name);
			FileSystem.writeAll(path.getAbsolutePath() + "/src/" + name + ".java", text); // re-add the extension
		}
	}

	private class ActiveObject extends Thread {

		private boolean running = true;
		private boolean mustRefresh = true;

		public void shutdown() {
			running = false;
		}

		public void refresh() {
			mustRefresh = true;
		}

		@Override
		public void run() {
			while (running) {
				try {
					// get latest code from source code text
					// save to file
					Set<String> names = null;
					synchronized (files) {
						names = files.keySet();
					}
					for (String name : names) {
						Programmable prog = files.get(name);
						if (prog.readyToSave() || mustRefresh) { // code is ready to be saved and compiled/checked
							mustRefresh = false;
							String text = prog.getCode();
							System.out.println("saving to " + name);
							FileSystem.writeAll(path.getAbsolutePath() + "/src/" + name + ".java", text); // re-add the extension

							// delete the "old files" in the class directory
							String projectPath = getPath().getAbsolutePath();
							String result = null;
							File[] allFiles = new File(getPath(), "class").listFiles(new FilenameFilter() {

								public boolean accept(File dir, String name) {
									return name.endsWith(".class");
								}
							});
							for (File file : allFiles) {
								String filename = file.getName();

								if (names.contains(filename.split("\\.")[0])) { // ignoring the ".class" extension here!
									continue; // ignore "current" files
								} else {
									System.out.println("System\\runsilent del /Q /F \"" + projectPath + "\\class\\" + filename + "\"");
									result = Execute.process("System\\runsilent del /Q /F \"" + projectPath + "\\class\\" + filename + "\"");
									System.out.println("result: " + result);
								}
							}

							result = Execute.process("JDK\\bin\\javac -target 1.3 " +
								"-source 1.3 -classpath JME\\lib\\cldc_1.1.jar;JME\\lib\\midp_2.0.jar;\"" +
								path.getAbsolutePath() + "\\class\";" +
								"System\\H2O.jar -d \"" + path.getAbsolutePath() +
								"\\class\" \"" + path.getAbsolutePath() + "\\src\\" + name + ".java\"");

							System.out.println("Program run result: " + result);

							if (result != null) {
								if (result.length() > 0) { // there was an error
									// so generate an error() message

									Pattern pattern = Pattern.compile(":([\\d]+):(.+)\\n"); // this pattern has 2 group
									Matcher matcher = pattern.matcher(result);

									if (matcher != null && matcher.find()) {
										String str = matcher.group(1); // get the str for the group match... ie, group 1
										// NOTE: group() is the total group, in this case :<num>:
										int lineNumber = Integer.parseInt(str);
										str = matcher.group(2); // get the str for the group match... ie, group 1
										// NOTE: group() is the total group, in this case :<num>:
										Error info = new Error(name, lineNumber, str);
										prog.error(info);
									} else {
										Error info = Error.UNKNOWN;
										prog.error(info);
									}

								} else {
									prog.error(Error.NONE);
								}
							} else {
								prog.error(Error.UNKNOWN);
							}

							prog.updated();
						} else if (prog.codeChanged()) {
							prog.error(Error.BLANK);
						}
					}
				} catch (Throwable t) {
					JOptionPane.showMessageDialog(null, "bug? " + t);
				}

				try {
					Thread.sleep(10);
				} catch (Throwable t) {
					shutdown();
				}
			}
		}
	}
}
